<?php

class NewNamesFinder
{
    public function __construct()
    {
        $this->initialize();
    }
    
    public function initialize()
    {
        if(!defined('UPPER')) define("UPPER","A-ZÀÂÅÅÃÄÁÆČÇÉÈÊËÍÌÎÏÑÓÒÔØÕÖÚÙÛÜßĶŘŠŞŽŒ");
        if(!defined('LOWER')) define("LOWER","a-záááàâåãäăæčćçéèêëĕíìîïǐĭñńóòôøõöŏúùûüůśšşřğžźýýÿœœ");
        if(!defined('STOP_KEYWORD')) define("STOP_KEYWORD",  "asdfib3r234");
        
        $this->uninomial_ranks = array( 'gen'           => 'genus',
                                        'genus'         => 'genus',
                                        'subgen'        => 'subgenus',
                                        'fam'           => 'family',
                                        'subfam'        => 'subfamily',
                                        'superfam'      => 'superfamily',
                                        'tribe'         => 'tribe',
                                        'subtribe'      => 'subtribe',
                                        'supertribe'    => 'supertribe',
                                        'class'         => 'class',
                                        'ord'           => 'order',
                                        'ordo'          => 'order',
                                        'order'         => 'order',
                                        'subord'        => 'suborder',
                                        'subordo'       => 'suborder',
                                        'suborder'      => 'suborder',
                                        'infraorder'    => 'infraorder',
                                        'superorder'    => 'superorder',
                                        'class'         => 'class',
                                        'subclass'      => 'subclass',
                                        'superclass'    => 'superclass',
                                        'nom'           => 'name',
                                        'stat'          => 'status');
        
        $this->binomial_ranks = array(  'sp'            => 'species',
                                        'species'       => 'species',
                                        'genus'         => 'genus',
                                        'gen'           => 'genus',
                                        'comb'          => 'combination',
                                        'nom'           => 'name',
                                        'stat'          => 'status');
        
        $this->nov_terms = array(       'nov',
                                        'n',
                                        'nova');
        
        
        
        // (? rank.? nov.? | nov.? rank.? )?
        $this->uninomial_rank_regex = "\(? ?(((".implode(array_keys($this->uninomial_ranks), "|").")\.? (".implode($this->nov_terms, "|").")\.? |(".implode($this->nov_terms, "|").")\.? (".implode(array_keys($this->uninomial_ranks), "|").")\.? )+) ?\)?";
        $this->binomial_rank_regex = "\(? ?(((".implode(array_keys($this->binomial_ranks), "|").")\.? (".implode($this->nov_terms, "|").")\.? |(".implode($this->nov_terms, "|").")\.? (".implode(array_keys($this->binomial_ranks), "|").")\.? )+) ?\)?";
    }
    
    public function find_names($html)
    {
        $this->names_found = array();
        $text = self::prepare_text($html);
        
        
        // $tokens = self::explode_html($html);
        // $elements = self::remove_tags_from_elements($tokens);
        // 
        // $text = "";
        // foreach($elements as $index => $element)
        // {
        //     $word = trim($element[0]);
        //     $text .= "$word ";
        // }        
        // $text = str_replace(";", STOP_KEYWORD, $text);
        // $text = preg_replace("/(\n|\r|\t|,|:|;)/", " ", $text);
        // $text = str_replace("&nbsp;", " ", $text);
        // $text = str_replace("&amp;", "&", $text);
        // $text = str_replace(" et ", " nov ", $text);            // for cases like: Genus species gen et sp nov
        // while(preg_match("/  /",$text)) $text = str_replace("  "," ",$text);
        
        
        
        // Namestring nov rank || Namestring rank nov
        // case insensitive in case NOV or RANK are capitalized
        if(preg_match_all("/ ([".UPPER."][".UPPER.LOWER."-]{2,}) $this->uninomial_rank_regex/i", $text, $arr, PREG_SET_ORDER))
        {
            $this->check_matches($arr);
        }
        
        // Genus species nov rank || Genus species rank noc
        // case insensitive in case NOV or RANK are capitalized
        if(preg_match_all("/ ([".UPPER."][".UPPER.LOWER."-]{2,} [".UPPER.LOWER."-]{2,}) $this->binomial_rank_regex/i", $text, $arr, PREG_SET_ORDER))
        {
            $this->check_matches($arr);
        }
        
        // Genus species nov rank || Genus species rank noc
        // case insensitive in case NOV or RANK are capitalized
        if(preg_match_all("/ ([".UPPER."][".UPPER.LOWER."-]{0,2}\. [".UPPER.LOWER."-]{2,}) $this->binomial_rank_regex/i", $text, $arr, PREG_SET_ORDER))
        {
            $this->check_matches($arr);
        }
        
        return $this->names_found;
    }
    
    public function check_matches($matches)
    {
        foreach($matches as $match)
        {
            $name = trim($match[1]);
            $ranks = str_replace(".", "", $match[2]);
            $ranks = trim(str_replace("  ", " ", $ranks));
            $ranks = explode(" ", $ranks);
            $rank_array = array();
            foreach($ranks as $rank)
            {
                $rank = strtolower($rank);
                if(in_array($rank, $this->nov_terms)) continue;
                if(isset($this->uninomial_ranks[$rank])) $rank_array[$this->uninomial_ranks[$rank]] = 1;
                elseif(isset($this->binomial_ranks[$rank])) $rank_array[$this->binomial_ranks[$rank]] = 1;
            }
            
            // abbreviated name - Hom. sapiens
            if(preg_match("/^[".UPPER."][".LOWER."-]{0,2}\. [".LOWER." -]+$/", $name))
            {
                $this->names_found[$name] = implode(" ", array_keys($rank_array));
            }
            // Uppercase lowercase
            elseif(preg_match("/^[".UPPER."][".LOWER." -]+$/", $name))
            {
                $this->names_found[$name] = implode(" ", array_keys($rank_array));
            }
            // ALLCAPS
            elseif(preg_match("/^([".UPPER."])([".UPPER." -]+)$/", $name, $arr))
            {
                $name = $arr[1] . strtolower($arr[2]);
                $this->names_found[$name] = implode(" ", array_keys($rank_array));
            }
        }
    }
    
    
    
    
    
    
    
    
    
    
    
    
    
    // // static functions
    
    public static function prepare_text(&$html)
    {
        // adding extra bits to catch names at the very end of the file
        $html .= " ".STOP_KEYWORD." ".STOP_KEYWORD;
        $html = str_ireplace("&amp;", "&", $html);
        $html = str_ireplace("&nbsp;", " ", $html);
        $html = str_ireplace("<", " <", $html);
        $html = str_ireplace(">", "> ", $html);
        $html = str_ireplace("(", " (", $html);
        $html = str_ireplace(")", ") ", $html);
        $html = str_ireplace("\t", " ".STOP_KEYWORD." ", $html);
        $html = str_ireplace("\n", " ".STOP_KEYWORD." ", $html);
        $html = str_ireplace("\r", " ".STOP_KEYWORD." ", $html);
        $html = str_ireplace(";", " ".STOP_KEYWORD." ", $html);
        $html = str_ireplace(".", " ", $html);
        $html = str_ireplace(",", " ", $html);
        $html = str_ireplace(":", " ", $html);
        $html = str_ireplace(" et ", " nov ", $html);
        $html = str_ireplace("«", " ", $html);
        while(preg_match("/  /",$html)) $html = str_replace("  "," ",$html);
        return $html;
        
        //return implode(" ", preg_split("/( |&nbsp;|<|>|\t|\n|\r|;|\.)/i", $html));
    }
    
    
    // public static function explode_html(&$html)
    // {
    //     $words_temp = preg_split("/( |&nbsp;|<|>|\t|\n|\r|;|\.)/i", $html, null, PREG_SPLIT_DELIM_CAPTURE);
    //     unset($html);
    //     
    //     $words = array();
    //     $n = 0;
    //     foreach($words_temp as $key => $element)
    //     {
    //         $word = $element[0];
    //         $offset = $element[1];
    //         
    //         if($word == "<")
    //         {
    //             $words[$n] = $element;
    //         }elseif($word == ">")
    //         {
    //             $words[$n-1] = self::merge_exploded_elements($words[$n-1], $element);
    //         }elseif($word==".")
    //         {
    //             $words[$n-1] = self::merge_exploded_elements($words[$n-1], $element);
    //         }elseif($word==";")
    //         {
    //             $words[$n-1] = self::merge_exploded_elements($words[$n-1], $element);
    //         }elseif(preg_match("/^[[:space:]]*$/",$word) && $n!=0)
    //         {
    //             $words[$n-1] = self::merge_exploded_elements($words[$n-1], $element);
    //         }else
    //         {
    //             @$words[$n] = self::merge_exploded_elements($words[$n], $element);
    //             $n++;
    //         }
    //         unset($words_temp[$key]);
    //     }
    //     
    //     return $words;
    // }
    //     
    //     public static function merge_exploded_elements($a, $b)
    //     {
    //         $new_element = array();
    //         $new_element[0] = $a[0] . $b[0];
    //         if(!$a) $new_element[1] = $b[1];
    //         else $new_element[1] = $a[1];
    //         
    //         return $new_element;
    //     }
    //     
    //     public static function remove_tags_from_elements($elements)
    //     {
    //         $cleaned_elements = array();
    //         
    //         $within_tag = false;
    //         foreach($elements as $element)
    //         {
    //             $word = trim($element[0]);
    //             
    //             if($within_tag)
    //             {
    //                 // ...tag>
    //                 if(preg_match("/^(.*)>\z/ims", $word, $arr))
    //                 {
    //                     if($e = self::get_element_from_tag($arr[1])) $cleaned_elements[] = $e;
    //                     $within_tag = false;
    //                 }
    //                 continue;
    //             }
    //             
    //             // <tag>
    //             if(preg_match("/^<(\/?.*[a-z0-9-]\/?)>\z/ims", $word, $arr))
    //             {
    //                 if($e = self::get_element_from_tag($arr[1])) $cleaned_elements[] = $e;
    //                 continue;
    //             }
    //             
    //             // <tag...
    //             if(preg_match("/^<([a-z0-9!].*)\z/ims", $word, $arr))
    //             {
    //                 if($e = self::get_element_from_tag($arr[1])) $cleaned_elements[] = $e;
    //                 $within_tag = true;
    //                 continue;
    //             }
    //             
    //             $cleaned_elements[] = $element;
    //         }
    //         
    //         $cleaned_elements[] = array(STOP_KEYWORD, -1);
    //         
    //         return $cleaned_elements;
    //     }
    //     
    //     private static function get_element_from_tag($tag)
    //     {
    //         $tag = strtolower($tag);
    //         if(substr($tag, 0, 1) == "/") $tag = substr($tag, 1);
    //         
    //         if($tag == "p" || $tag == "td" || $tag == "tr" || $tag == "table" || $tag == "hr")
    //         {
    //             return array(STOP_KEYWORD, -1);
    //         }
    //         
    //         return false;
    //     }
}

?>